using System;
using System.Collections;
using System.Collections.Generic;


namespace LightCAD.Three
{
    public class ArrowHelper : Object3D
    {
        #region scope properties or methods
        //private static Vector3 _axis = new Vector3();
        private static BufferGeometry _lineGeometry;
        private static CylinderGeometry _coneGeometry;
        private static ArrowHelperContext getContext()
        {
            return ThreeThreadContext.GetCurrThreadContext().ArrowHelperCtx;
        }
        #endregion

        #region Properties

        public Line line;
        public Mesh cone;

        #endregion

        #region constructor
        public ArrowHelper(Vector3 dir = null, Vector3 origin = null, double length = 1, int color = 0xffff00, double headLength = double.NaN, double headWidth = double.NaN)
        {
            if (dir == null) dir = new Vector3(0, 0, 1);
            if (origin == null) origin = new Vector3(0, 0, 0);
            if (headLength == double.NaN) headLength = length * 0.2;
            if (headWidth == double.NaN) headWidth = headLength * 0.2;

            this.type = "ArrowHelper";

            if (_lineGeometry == null)
            {
                _lineGeometry = new BufferGeometry();
                _lineGeometry.setAttribute("position", new Float32BufferAttribute(new double[] { 0, 0, 0, 0, 1, 0 }, 3));
                _coneGeometry = new CylinderGeometry(0, 0.5, 1, 5, 1);
                _coneGeometry.translate(0, -0.5, 0);
            }
            this.position.Copy(origin);
            this.line = new Line(_lineGeometry, new LineBasicMaterial{ color = new Color(color), toneMapped = false });
            this.line.matrixAutoUpdate = false;
            this.add(this.line);
            this.cone = new Mesh(_coneGeometry, new MeshBasicMaterial{ color = new Color(color), toneMapped = false });
            this.cone.matrixAutoUpdate = false;
            this.add(this.cone);
            this.setDirection(dir);
            this.setLength(length, headLength, headWidth);
        }
        #endregion

        #region methods
        public void setDirection(Vector3 dir)
        {
            // dir is assumed to be normalized
            if (dir.Y > 0.99999)
            {
                this.quaternion.Set(0, 0, 0, 1);
            }
            else if (dir.Y < -0.99999)
            {
                this.quaternion.Set(1, 0, 0, 0);
            }
            else
            {
                var _axis = getContext()._axis;
                _axis.Set(dir.Z, 0, -dir.X).Normalize();
                var radians = Math.Acos(dir.Y);
                this.quaternion.SetFromAxisAngle(_axis, radians);
            }
        }
        public void setLength(double length, double headLength = double.NaN, double headWidth = double.NaN)
        {
            if (headLength == double.NaN) headLength = length * 0.2;
            if (headWidth == double.NaN) headWidth = headLength * 0.2;

            this.line.scale.Set(1, Math.Max(0.0001, length - headLength), 1); // see #17458
            this.line.updateMatrix();
            this.cone.scale.Set(headWidth, headLength, headWidth);
            this.cone.position.Y = length;
            this.cone.updateMatrix();
        }

        public void setColor(object color)
        {
            (this.line.material as LineBasicMaterial).color.Set(color);
            (this.cone.material as MeshBasicMaterial).color.Set(color);
        }
        public ArrowHelper copy(ArrowHelper source)
        {
            base.copy(source, false);
            this.line.copy(source.line);
            this.cone.copy(source.cone);
            return this;
        }
        public void dispose()
        {
            this.line.geometry.dispose();
            this.line.material.dispose();
            this.cone.geometry.dispose();
            this.cone.material.dispose();
        }
        #endregion

    }
}
